<!DOCTYPE html>
<html lang="en">
	<head>
		<meta charset="UTF-8">
		<title></title>
	</head>
	<body>
		<script>
			{
				/*
				  函数缓存
					memoize函数接受一个函数作为参数，并返回一个新的函数。新的函数会先检查调用参数是否存在于缓存中，如果存在，则直接返回缓存结果；如果不存在，则调用原始函数计算结果，并将结果存入缓存中后再返回。
					函数缓存适用于纯函数，即相同的输入始终产生相同的输出。对于有副作用的函数或依赖外部状态的函数，使用函数缓存可能会导致不符合预期的结果。因此，在应用函数缓存时，需要仔细考虑函数的纯度和可缓存性。
				*/
				function memoize(func) {
					const cache = Object.create(null);
					const self = this;
					return (...args) => {
						const key = JSON.stringify(args);
						if (cache[key]) {
							console.log(cache, 'cache2')
							return cache[key];
						}
						const result = func.apply(self, args);
						cache[key] = result;
						console.log(cache, 'cache1')
						return result;
					};
				}
				const fn = memoize(function(x, y) {
					console.log(x + y, 'memoize')
					return x + y
				})
				fn(1, 5)
				fn(2, 6)
				fn(2, 6)
			}

			{
				// 通用型柯里化
				function currying(fn) {
					// 未柯里化函数所需的参数个数
					var limit = fn.length
					var params = [] // 存储递归过程的所有参数，用于递归出口计算值
					return function _curry(...args) {
						params = params.concat(...args) // 收集递归参数
						if (limit <= params.length) {
							let tempParams = params.slice(0, limit)
							if (limit === params.length) { //参数个数满足时清除已缓存的参数
								params = []
							}
							// 返回函数执行结果
							return fn.apply(null, tempParams)
						} else {
							// 返回一个柯里化函数
							return _curry
						}
					}
				}
				function add(x, y, z) {
					return x + y + z;
				}
				// 函数柯里化
				var addCurried = currying(add);
				console.log(`addCurried(1)(2)(3)`, addCurried(1)(2)(3)) // 6
				console.log(`addCurried(3,3,3)`, addCurried(3, 3, 3)) // 9
				console.log(`addCurried(1,2)(3)`, addCurried(1, 2)(3)) // 6
				console.log(`addCurried(3)(4,5)`, addCurried(3)(4, 5)) // 12
			}

			{
				// 偏函数
				// 乘法
				let multi = (x, y) => x * y;
				// 构造一个对数值乘以2的函数
				let double = multi.bind(null, 2);
				console.log(double(3)); // 6
				console.log(double(5)); // 10
				/**
				 * 偏函数实现
				 * @param func 应用函数
				 * @param argsBound 固定参数
				 * @return {function(...[*]): *}
				 */
				let partial = (func, ...argsBound) => {
					if (typeof func !== 'function') throw new TypeError(
						`${typeof func} is not a function`)
					return function(...args) { // (*)
						if (func.length - argsBound.length > args.length) throw new Error(`miss arguments`)
						return func.call(this, ...argsBound.concat(...args))
					}
				}
				let partialMulti = partial(multi, 2)
				// console.log(partialMulti()); // Error: miss arguments
				console.log(partialMulti(3)); // 6
			}
		</script>
	</body>
</html>